/**
* @describe GUI组件类
* @author 游金宇(KM)
* @date 2024-09-12 11:46:22
*/

import { __private, _decorator, AssetManager, assetManager, Canvas, Component, Director, director, error, game, instantiate, Layers, log, Node, Prefab, Scene, SceneAsset, tween, Tween, UIOpacity, warn } from 'cc';
import { BaseComponent, IUIOption } from './base/BaseComponent';
import { ICloseOptions, IOpenOptions, UILayer } from './layer/UILayer';
import { CoreToast, ToastProps, ToastType } from '../ui/toast/Toast';
import { GlobalEventConstant } from '../constant';
import { CoreUIContainer } from './components/UIContainer';
import { CoreShowLoading, CoreShowLoadingProps } from '../ui/show-loading/ShowLoading';
import { CoreReconnection } from '../ui/reconnection/Reconnection';
import { CoreNotice, CoreNoticeProps } from '../ui/notice/Notice';
import { SceneDataType, ScenePropsType, SceneLayer } from './scene';
import { Manager } from '../manager';
import { ReconnectPrompt } from '../ui/reconnection/ReconnectPrompt';

export { ReconnectPrompt }


const { ccclass, property, menu } = _decorator;

type UIComponentType<T> = T extends UILayer<infer U, infer K> ? { props: U, data: K } : { props: object, data: object };

type PropsType<T> = UIComponentType<T> extends { props: infer U extends object, data: infer K extends object } ? U : never;

type DataType<T> = UIComponentType<T> extends { props: infer U extends object, data: infer K extends object } ? K : never;


export enum LayerType {
    UI,
    /**加载 */
    LOADING,
    /**提示 */
    TOAST,
    /**断线重连 */
    RECONNECTTION,
    /**系统服务通知(停机维护) */
    NOTICE
}


/**Gui层 */
@ccclass('Gui')
@menu("CATCORE/Gui")
export class Gui extends BaseComponent {

    @property({ type: Prefab, tooltip: '断线重连UI预制体' })
    reconnection_ui_prefab: Prefab

    @property({ type: Prefab, tooltip: '提示UI预制体' })
    toast_ui_prefab: Prefab

    @property({ type: Prefab, tooltip: '加载UI预制体' })
    loading_ui_prefab: Prefab

    @property({ type: Prefab, tooltip: '公告UI预制体' })
    notice_ui_prefab: Prefab

    @property({ type: Prefab, tooltip: 'UI层预制体' })
    ui_prefab: Prefab

    @property({ type: Node, tooltip: 'root-UI层' })
    root_ui: Node

    @property({ type: Node, tooltip: 'root-组件层' })
    root_toast: Node

    @property({ type: Node, tooltip: 'root-mask' })
    root_mask: Node

    /**场景中的UI层 */
    private ui_container_component: CoreUIContainer | null

    /**断线重连UI弹窗 */
    private reconnection_ui_component: CoreReconnection | null

    /**公告UI弹窗组件 */
    private notice_ui_component: CoreNotice

    /**加载UI弹窗 */
    private loading_ui_component: CoreShowLoading | null

    /**提示UI弹窗 */
    private toast_ui_component: CoreToast

    /**当前场景对象名 */
    public currentScene: string;

    /**当前场景uuid */
    public currentSceneUUID: string;

    protected override onEventListener(): void {
        this.cat.event.on(GlobalEventConstant.ROOT_MASK_UPDATE, this.onRootUpdate, this)
    }

    init(cat: Manager) {
        this.cat = cat
        const scene = director.getScene()
        log('init scene')
        scene && this.changeScene(scene);
        return this
    }

    protected override start(): void {
        this.onRootUpdate()
    }

    private toastQueue: ToastProps[] = [];
    private isScheduling: boolean = false; // 新增调度锁
    private minInterval: number = 0.2; // 改为秒单位，与scheduleOnce一致

    showToast(props: ToastProps) {
        this.toastQueue.push(props);
        if (!this.isScheduling) {
            this.processQueueWithInterval();
        }
    }

    private processQueueWithInterval() {
        if (this.toastQueue.length === 0) {
            this.isScheduling = false;
            return;
        }

        this.isScheduling = true;
        const props = this.toastQueue.shift()!;

        // 实际显示逻辑
        const show = () => {
            const node = instantiate(this.toast_ui_prefab);
            const toastComponent = node.getComponent(CoreToast)!;
            toastComponent.addToParent(this.root_toast, { props });

            // 使用游戏时间计算下一个
            this.scheduleOnce(() => {
                this.processQueueWithInterval();
            }, this.minInterval);
        };

        // 立即显示第一个
        if (this.root_toast.children.length === 0) {
            show();
        } else {
            this.scheduleOnce(show, this.minInterval);
        }
    }

    /** 隐藏并清除所有提示 */
    hideToast() {
        // 1. 销毁正在显示的Toast
        if (this.root_toast) {
            this.root_toast.children.forEach(child => {
                child.getComponent(CoreToast)?.removeAndDestroy()
            });
        }

        // 2. 清空等待队列
        this.toastQueue = [];

        // 3. 取消预定的队列处理
        this.unschedule(this.processQueueWithInterval);
        this.isScheduling = false;

        return this;
    }

    /**显示Loading */
    showLoading({
        title = '',
        mask = true,
        black = true
    }: CoreShowLoadingProps = {}) {
        log('showLoading', title)
        if (this.loading_ui_component) {
            this.loading_ui_component.setOptions({
                props: {
                    title,
                    mask,
                    black
                }
            })
        } else {
            const node = instantiate(this.loading_ui_prefab)
            this.loading_ui_component = node.getComponent(CoreShowLoading)!.addToParent(this.root_ui, {
                props: {
                    title,
                    mask,
                    black
                }
            })
        }
        return this
    }

    /**隐藏Loading */
    hideLoading() {
        this.loading_ui_component?.removeAndDestroy()
        this.loading_ui_component = null
        return this
    }

    /**
     * 显示断线重连 
     * @param option 状态提示文案
     */
    showReconnect(option: ReconnectPrompt) {
        if (!this.reconnection_ui_component) {
            const node = instantiate(this.reconnection_ui_prefab)
            this.reconnection_ui_component = node.getComponent(CoreReconnection)!.addToParent(this.root_ui, {
                props: {
                    content: option
                }
            })
        } else {
            this.reconnection_ui_component.setUpdateProps({
                content: option
            })
        }
        return this
    }

    /**隐藏断线重连 */
    hideReconnect() {
        this.reconnection_ui_component?.removeAndDestroy()
        this.reconnection_ui_component = null
        return this
    }

    /**
     * 显示公告
     * @param option 参数
     */
    showNotice(option: CoreNoticeProps) {
        const node = instantiate(this.notice_ui_prefab)
        this.notice_ui_component = node.getComponent(CoreNotice)!.addToParent(this.root_ui, { props: option })
        return this
    }

    /**隐藏公告 */
    hideNotice() {
        this.notice_ui_component?.removeAndDestroy()
        return this
    }

    findBundleBySceneUUID(sceneUUID: string): AssetManager.Bundle | null {
        const bundlesMap = (assetManager.bundles as any)._map as Record<string, AssetManager.Bundle>

        for (const key in bundlesMap) {
            const bundle = bundlesMap[key]

            const sceneMap = ((bundle as any).config)?.scenes?._map

            if (!sceneMap) continue

            for (const sceneKey in sceneMap) {
                const sceneInfo = sceneMap[sceneKey]
                if (sceneInfo?.uuid === sceneUUID) {
                    return bundle
                }
            }
        }

        return null
    }

    findSceneByUUIDFromBundle(bundle: AssetManager.Bundle, sceneUUID: string) {
        return
    }

    /**
     * 加载并切换游戏场景
     * @param sceneName 场景名称、场景实例或场景资源，用于指定要加载的场景
     * @param options 可选参数，可以是场景加载的配置选项，或者是一个表示是否重新加载的布尔值
     * @param isReload 可选参数，明确指示是否重新加载场景（当options为布尔值时，此参数被忽略）
     * @returns 返回当前实例，支持链式调用
     */
    // 1. 仅场景名
    loadScene<T>(scene: string | Scene | SceneAsset): this;

    // 2. 场景名 + options
    loadScene<T>(scene: string | Scene | SceneAsset, options: IUIOption<ScenePropsType<T>, SceneDataType<T>>): this;

    // 3. 场景名 + reload
    loadScene<T>(scene: string | Scene | SceneAsset, isReload: boolean): this;

    // 4. 场景名 + 回调
    loadScene<T>(scene: string | Scene | SceneAsset, onLaunched: () => void): this;

    // 5. 场景名 + options + reload
    loadScene<T>(
        scene: string | Scene | SceneAsset,
        options: IUIOption<ScenePropsType<T>, SceneDataType<T>>,
        isReload: boolean
    ): this;

    // 6. 场景名 + options + 回调
    loadScene<T>(
        scene: string | Scene | SceneAsset,
        options: IUIOption<ScenePropsType<T>, SceneDataType<T>>,
        onLaunched: () => void
    ): this;

    // 7. 场景名 + options + reload + 回调
    loadScene<T>(
        scene: string | Scene | SceneAsset,
        options: IUIOption<ScenePropsType<T>, SceneDataType<T>>,
        isReload: boolean,
        onLaunched: () => void
    ): this;

    // 8. 场景名 + reload + 回调
    loadScene<T>(
        scene: string | Scene | SceneAsset,
        isReload: boolean,
        onLaunched: () => void
    ): this;

    // ---------------------- 实现部分 ----------------------
    loadScene<T>(
        scene: string | Scene | SceneAsset,
        arg2?: IUIOption<ScenePropsType<T>, SceneDataType<T>> | boolean | (() => void),
        arg3?: boolean | (() => void),
        arg4?: () => void
    ): this {
        log('[加载场景参数]', scene, arg2, arg3, arg4);

        let options: IUIOption<ScenePropsType<T>, SceneDataType<T>> | undefined;
        let isReload = true;
        let onLaunched: (() => void) | undefined;

        // --- 参数自动识别 ---
        const args = [arg2, arg3, arg4];
        for (const arg of args) {
            if (typeof arg === 'boolean') isReload = arg;
            else if (typeof arg === 'function') onLaunched = arg;
            else if (arg && typeof arg === 'object') options = arg;
        }

        // --- 监听场景事件 ---
        director.once(Director.EVENT_BEFORE_SCENE_LAUNCH, (sceneObj: Scene) => {
            this.changeScene(sceneObj, options ?? {}, isReload);
        });

        director.once(Director.EVENT_AFTER_SCENE_LAUNCH, () => {
            onLaunched?.();
        });

        // --- 加载场景 ---
        if (typeof scene === 'string') {
            const bundle = this.findBundleBySceneUUID(scene);
            if (bundle) {
                if (bundle.name !== 'main') {
                    bundle.loadScene(this.currentScene, (err, loadedScene) => {
                        if (err) return error('loadScene err', err);
                        director.runScene(loadedScene);
                    });
                } else {
                    const asset = bundle.getAssetInfo(scene) as
                        | __private._cocos_asset_asset_manager_config__ISceneInfo
                        | null;
                    if (asset?.url) director.loadScene(asset.url);
                }
            } else {
                director.loadScene(scene);
            }
        } else {
            director.runScene(scene);
        }

        return this;
    }


    /**重置场景(清除当前默认当前场景) */
    resetScene(sceneNameOrUUID: string = '') {
        sceneNameOrUUID = sceneNameOrUUID || this.currentScene
        return this.loadScene(sceneNameOrUUID)
    }

    /**清除场景 */
    cleanScene() {
        // 清理全局UI
        this.resetScene().hideLoading().hideNotice().hideReconnect().hideToast()
    }

    /**
     * 场景变化
    * @param scene 场景
    * @param options 选项
    * @param isReload 是否重新加载
    */
    changeScene<T>(scene: Scene, options?: IUIOption<ScenePropsType<T>, SceneDataType<T>>, isReload?: boolean) {

        this.currentScene = scene.name;
        this.currentSceneUUID = scene.uuid

        // 获取当前场景
        // const canvas = scene.getComponentsInChildren(Canvas);
        // let ui2dCanvas = canvas.find(item => {
        //     // return item.node.layer == Layers.Enum.UI_2D;
        //     // UI_IN_SCENE
        //     return item.node.layer == 2 ** 15;
        // });
        // if (!ui2dCanvas) {
        //     ui2dCanvas = canvas.find(item => {
        //         return item.node.layer == Layers.Enum.UI_2D;
        //     });
        // }
        // const canvasNode = ui2dCanvas!.node;

        // 创建UI层
        this.createUILayer(scene)

        // 给场景传递属性值
        const crrentScene = scene.getComponentInChildren(SceneLayer);
        log('给场景传递属性值=======', crrentScene)
        if (crrentScene) {
            crrentScene.isReload = isReload ?? true
        }

        // 更新场景props
        crrentScene?.setOptions(options)
    }

    private createUILayer(scene: Scene) {
        this.ui_container_component?.removeAndDestroy()
        this.ui_container_component = null

        const node = instantiate(this.ui_prefab)
        this.ui_container_component = node.getComponent(CoreUIContainer)
        this.ui_container_component?.setGui(this)!.addToParent(scene, this.node.getSiblingIndex())
    }

    /**重新加载当前场景 */
    reloadScene(onLaunched: (() => void) = () => { }) {
        log('重新加载当前场景')
        this.loadScene(this.currentSceneUUID, true, onLaunched)
    }


    /**关闭UI */
    async closeUI<T extends UILayer<object>, U extends object>(ui: Node | T, options?: ICloseOptions<T, U>) {
        const { component } = this.findUIBaseLayer(ui, false);
        if (component) {
            component.setOptions({
                ...(options?.hook ?? {}),
                ...(options?.props ?? {})
            })
            // 调用uiNode组件下的showTween方法
            await component.hideTween<T, U>(options || {});
            // 移除层级遮罩
            this.ui_container_component?.subMask()
        }
    }


    /**
     * 打开ui层
     * @param ui 界面的节点或组件
     * @param options 动画参数选项
     */

    async openUI<T extends UILayer>(ui: Node, options?: IOpenOptions<PropsType<T>, DataType<T>>) {
        // 查询根节点
        const { rootNode, component } = this.findUIBaseLayer(ui, true);
        // 获取根组件
        const uilayer = rootNode.getComponent(UILayer)
        // 配置选项
        component?.setOptions(options)

        // 动画
        if (rootNode) {
            // 添加层级遮罩
            this.ui_container_component?.addNodeToTween(rootNode).addMask()
        }

        if (component) {
            // 调用uiNode组件下的showTween方法
            await component.showTween(options || {});
        }
        // 运动结束之后，添加界面到UI层
        this.ui_container_component?.ui_container && component?.addToParent(this.ui_container_component.ui_container)
    }

    /**根据节点或组件 查询根UI层(继承自UILayer的)节点或组件 */
    private findUIBaseLayer<T extends UILayer>(ui: Node | T, isOpen: boolean): { rootNode: Node; component: T | null } {
        let rootNode: Node;
        let component: T | null = null;
        if (ui instanceof Node) {
            // ui为节点
            rootNode = ui;
            // 获取组件
            const extendsBaseLayer = ui.components.filter(item => {
                return (item instanceof UILayer);
            }) as T[];
            if (extendsBaseLayer.length) {
                if (extendsBaseLayer.length == 1) {
                    component = extendsBaseLayer[0];
                } else {
                    warn(`${ui.name}节点存在多个继承自BaseLayer的组件`);
                }
            } else {
                error(`${ui.name}节点未找到继承自BaseLayer的组件`);
                return { rootNode, component };
            }
        } else {
            // ui为T组件
            // 定义一个变量用于保存根节点
            rootNode = ui.node;
            component = ui;
            if (isOpen) {
                // 循环向上查找根节点，直到找到没有父节点的节点
                while (rootNode.parent) {
                    rootNode = rootNode.parent;
                }
                component.root = rootNode;
            } else {
                rootNode = ui.root;
            }
        }
        return { rootNode, component };
    }


    /**根层的变化 */
    private onRootUpdate() {
        // 如果根ui节点 没有子节点 则隐藏根遮罩
        const last_active_index = this.root_ui.children.findLastIndex(item => item.active && item != this.root_mask)
        // 显示
        this.root_mask.active = last_active_index != -1
        // 调整层级
        if (last_active_index > -1) {
            // 更新根遮罩层级
            const last = this.root_ui.children[last_active_index];
            const nonce = last.getSiblingIndex() - 1
            this.root_mask.setSiblingIndex(nonce < 0 ? 0 : nonce)
        }
        // 通知ui层级变化
        this.cat.event.emit(GlobalEventConstant.ROOT_MASK_CHANGE)
    }

}


